/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
mxCodecRegistry.register(
  (function () {
    /**
     * Class: mxGraphViewCodec
     *
     * Custom encoder for <mxGraphView>s. This class is created
     * and registered dynamically at load time and used implicitely via
     * <mxCodec> and the <mxCodecRegistry>. This codec only writes views
     * into a XML format that can be used to create an image for
     * the graph, that is, it contains absolute coordinates with
     * computed perimeters, edge styles and cell styles.
     */
    var codec = new mxObjectCodec(new mxGraphView());

    /**
     * Function: encode
     *
     * Encodes the given <mxGraphView> using <encodeCell>
     * starting at the model's root. This returns the
     * top-level graph node of the recursive encoding.
     */
    codec.encode = function (enc, view) {
      return this.encodeCell(enc, view, view.graph.getModel().getRoot());
    };

    /**
     * Function: encodeCell
     *
     * Recursively encodes the specifed cell. Uses layer
     * as the default nodename. If the cell's parent is
     * null, then graph is used for the nodename. If
     * <mxGraphModel.isEdge> returns true for the cell,
     * then edge is used for the nodename, else if
     * <mxGraphModel.isVertex> returns true for the cell,
     * then vertex is used for the nodename.
     *
     * <mxGraph.getLabel> is used to create the label
     * attribute for the cell. For graph nodes and vertices
     * the bounds are encoded into x, y, width and height.
     * For edges the points are encoded into a points
     * attribute as a space-separated list of comma-separated
     * coordinate pairs (eg. x0,y0 x1,y1 ... xn,yn). All
     * values from the cell style are added as attribute
     * values to the node.
     */
    codec.encodeCell = function (enc, view, cell) {
      var model = view.graph.getModel();
      var state = view.getState(cell);
      var parent = model.getParent(cell);

      if (parent == null || state != null) {
        var childCount = model.getChildCount(cell);
        var geo = view.graph.getCellGeometry(cell);
        var name = null;

        if (parent == model.getRoot()) {
          name = "layer";
        } else if (parent == null) {
          name = "graph";
        } else if (model.isEdge(cell)) {
          name = "edge";
        } else if (childCount > 0 && geo != null) {
          name = "group";
        } else if (model.isVertex(cell)) {
          name = "vertex";
        }

        if (name != null) {
          var node = enc.document.createElement(name);
          var lab = view.graph.getLabel(cell);

          if (lab != null) {
            node.setAttribute("label", view.graph.getLabel(cell));

            if (view.graph.isHtmlLabel(cell)) {
              node.setAttribute("html", true);
            }
          }

          if (parent == null) {
            var bounds = view.getGraphBounds();

            if (bounds != null) {
              node.setAttribute("x", Math.round(bounds.x));
              node.setAttribute("y", Math.round(bounds.y));
              node.setAttribute("width", Math.round(bounds.width));
              node.setAttribute("height", Math.round(bounds.height));
            }

            node.setAttribute("scale", view.scale);
          } else if (state != null && geo != null) {
            // Writes each key, value in the style pair to an attribute
            for (var i in state.style) {
              var value = state.style[i];

              // Tries to turn objects and functions into strings
              if (typeof value == "function" && typeof value == "object") {
                value = mxStyleRegistry.getName(value);
              }

              if (
                value != null &&
                typeof value != "function" &&
                typeof value != "object"
              ) {
                node.setAttribute(i, value);
              }
            }

            var abs = state.absolutePoints;

            // Writes the list of points into one attribute
            if (abs != null && abs.length > 0) {
              var pts = Math.round(abs[0].x) + "," + Math.round(abs[0].y);

              for (var i = 1; i < abs.length; i++) {
                pts += " " + Math.round(abs[i].x) + "," + Math.round(abs[i].y);
              }

              node.setAttribute("points", pts);
            }

            // Writes the bounds into 4 attributes
            else {
              node.setAttribute("x", Math.round(state.x));
              node.setAttribute("y", Math.round(state.y));
              node.setAttribute("width", Math.round(state.width));
              node.setAttribute("height", Math.round(state.height));
            }

            var offset = state.absoluteOffset;

            // Writes the offset into 2 attributes
            if (offset != null) {
              if (offset.x != 0) {
                node.setAttribute("dx", Math.round(offset.x));
              }

              if (offset.y != 0) {
                node.setAttribute("dy", Math.round(offset.y));
              }
            }
          }

          for (var i = 0; i < childCount; i++) {
            var childNode = this.encodeCell(
              enc,
              view,
              model.getChildAt(cell, i)
            );

            if (childNode != null) {
              node.appendChild(childNode);
            }
          }
        }
      }

      return node;
    };

    // Returns the codec into the registry
    return codec;
  })()
);
